import pandas as pd
from gofishing.commodity import CommodityBase


class Indicator:

    indicators: dict[str, 'Indicator'] = {}

    def __init__(self, indicatorID: str, commodity: CommodityBase) -> None:
        if indicatorID in Indicator.indicators:
            raise Exception('indicatorID must be unique')
        else:
            self.indicatorID = indicatorID
            Indicator.indicators[self.indicatorID] = self

        self.commodity = commodity

    def updateData(self) -> None:
        raise NotImplementedError

    def update() -> None:
        indicators = Indicator.indicators
        for indicatorID in indicators:
            indicators[indicatorID].updateData()


class Donchian(Indicator):

    def __init__(self, indicatorID: str,
                 commodity: CommodityBase, period: int) -> None:
        super().__init__(indicatorID=indicatorID, commodity=commodity)
        self.period = period
        self.high = pd.Series(dtype='float32')
        self.low = pd.Series(dtype='float32')

    def updateData(self) -> None:
        self.high = self.commodity.high.rolling(self.period).max().dropna()
        self.low = self.commodity.low.rolling(self.period).min().dropna()


class TR(Indicator):

    def __init__(self, indicatorID: str, commodity: CommodityBase) -> None:
        super().__init__(indicatorID, commodity)
        self.tr = pd.DataFrame()

    def updateData(self) -> None:
        high = self.commodity.high
        low = self.commodity.low
        closeBefore = self.commodity.close.shift(periods=1).dropna()
        self.tr = pd.DataFrame({
            'rangeA': (high - low),
            'rangeB': (closeBefore - high).abs(),
            'rangeC': (closeBefore - low).abs()}).max(axis=1)[1:]


class ATR(TR):
    def __init__(self, indicatorID: str, commodity: CommodityBase,
                 period=14) -> None:
        super().__init__(indicatorID, commodity)
        self.period = period
        self.atr = pd.DataFrame()

    def updateData(self) -> None:
        super().updateData()
        self.atr = self.tr.rolling(self.period).mean().dropna()
